package com.cloud.rou.im.client.bootstrap;

import com.cloud.rou.im.client.api.ClientListener;
import com.cloud.rou.im.client.api.event.DefaultClientListener;
import com.cloud.rou.im.client.context.UserContext;
import com.cloud.rou.im.client.context.UserContextHolder;
import com.cloud.rou.im.client.handler.BizHandler;
import com.cloud.rou.im.client.msg.ChatMsgDeliveredQueueExecutor;
import com.cloud.rou.im.client.user.UserInfo;
import com.cloud.rou.im.core.exception.IMException;
import com.cloud.rou.im.msg.ClientMsg;
import com.cloud.rou.im.msg.enumtype.MsgType;
import com.cloud.rou.im.protocol.codec.DataPacketDecoder;
import com.cloud.rou.im.protocol.codec.DataPacketEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * <p></p>
 *
 * @author GaoShuangWen
 */
@Slf4j
public class ClientBootstrap {

    private ClientListener listener;

    private UserInfo userInfo;

    NioEventLoopGroup g;

    private ChannelFuture f;

    public ClientBootstrap(UserInfo userInfo) {
        this.userInfo = userInfo;
        this.listener = new DefaultClientListener();
        this.g = new NioEventLoopGroup();
    }

    public void addListener(ClientListener listener) {
        this.listener = listener;
    }

    public void start() {
        BizHandler handler = new BizHandler(listener);
        UserContext userContext = new UserContext();
        userContext.setUserId(userInfo.getUserId());
        userContext.setHandler(handler);
        UserContextHolder.addContext(userContext);
        doStart(handler);
        ClientMsg msg = ClientMsg.builder().msgType(MsgType.LOGIN).senderId(userInfo.getUserId()).build();
        UserContextHolder.writeAndFlush(msg);
        ChatMsgDeliveredQueueExecutor instance = new ChatMsgDeliveredQueueExecutor();
        instance.addListener(result -> {
          /*  if (!result.getSuccess()) {
                try {
                    f.channel().closeFuture().sync();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    g.shutdownGracefully();
                }
            } else {
                g.shutdownGracefully();
            }*/
            if (!result.getSuccess()) {
                g.shutdownGracefully();
            }
        });
        instance.start(msg);
    }


    public void doStart(BizHandler handler) {

        Bootstrap b = new Bootstrap();
        b.group(g);
        b.channel(NioSocketChannel.class);
        // b.option(ChannelOption.SO_KEEPALIVE, true);
        b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        b.remoteAddress(userInfo.getHost(), Integer.valueOf(userInfo.getPort()));
        // 设置通道初始化
        b.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) {
                ch.pipeline().addLast("decoder", new DataPacketDecoder());
                ch.pipeline().addLast("encoder", new DataPacketEncoder());
                ch.pipeline().addLast(handler);
            }
        });

        ChannelFuture f = b.connect();
        f.addListener(future -> {
            if (future.isSuccess()) {
                log.debug("连接成功");
            } else {
                throw new IMException("[client] connect to connector failed! connector url: "
                        + userInfo.getHost() + ":" + userInfo.getPort());
            }
        });
        try {
            this.f = f;
            f.get(10, TimeUnit.SECONDS);
            /*  f.channel().closeFuture().sync();*/
        } catch (InterruptedException | TimeoutException | ExecutionException e) {
            throw new IMException("[client] connect to connector failed!");
        }

    }
}
